package io.xxx.sync.dao

import com.baomidou.mybatisplus.annotation.TableId
import com.baomidou.mybatisplus.annotation.TableName
import com.baomidou.mybatisplus.core.toolkit.IdWorker
import io.xxx.sync.core.PageSynchronizer
import io.xxx.sync.core.formatter
import org.quartz.CronExpression
import org.quartz.CronScheduleBuilder
import org.quartz.TriggerBuilder
import org.quartz.TriggerKey
import org.slf4j.LoggerFactory
import java.time.LocalDateTime

@TableName("job_property")
data class JobProperty(
    @TableId val beanName: String,
    val description: String,
    val enabled: Boolean,
    val address: String,
    val cron: String
) {

    val trigger
        get() = if (!CronExpression.isValidExpression(cron)) {
            log.warn(
                "Job[{},{}] cron expression [{}] is not valid.",
                beanName, description, cron
            )
            null
        } else {
            TriggerBuilder.newTrigger()
                .withIdentity(TriggerKey(beanName))
                .withDescription(description)
                .withSchedule(CronScheduleBuilder.cronSchedule(cron))
                .build()
        }

    companion object {
        private val log = LoggerFactory.getLogger(JobProperty::class.java)
    }
}

@TableName("job_log")
data class JobLog(
    @TableId val id: Long,
    /**
     * 操作类型。1（启动）、2（已完成）、3（已失败）
     */
    val optType: Int,
    /**
     * 日志消息
     */
    val msg: String?,
    val created: LocalDateTime
)

@TableName("auth_info")
data class AuthInfo(
    @TableId
    val id: Long,

    val channel: String,

    val shopCode: String,

    val targetShopCode: String?,

    val appKey: String,

    val appSecret: String,

    val accessToken: String
)

@TableName("sync_property")
data class SyncProperty(
    @TableId
    val id: Long,
    /**
     * 渠道
     */
    val channel: String,
    /**
     * 1 - 订单
     * 2 - 退单
     */
    val type: Int?,
    /**
     * 门店编码
     */
    val shopCode: String?,
    /**
     * 任务的起源时间
     */
    val originTime: LocalDateTime,
    /**
     * 起始页码，默认为 1
     */
    val startPage: Int = 1,
    /**
     * 每页数据量/每次批处理的数据量大小
     */
    val pageSize: Int = 100,
    /**
     * 数据延迟时间，默认60秒
     */
    val delay: Int = 60,
    /**
     * 接口地址的host
     */
    val httpUrl: String?,
    /**
     * URL路径，多个路径使用","分隔
     */
    val urlPath: String?,
    /**
     * URL查询参数，多个参数使用","分隔
     */
    val urlQueryParams: String?,
    /**
     * Token name
     */
    val tokenName: String?,
    /**
     * Token value
     */
    val tokenValue: String?,
    /**
     * JSONPath for data
     */
    val dataJsonPath: String?,
    /**
     * JSONPath for sn
     */
    val snJsonPath: String?,
    /**
     * JSONPath for status
     */
    val statusJsonPath: String?,
    /**
     * JSONPath for receiver name
     */
    val receiverNameJsonPath: String?,
    /**
     * JSONPath for receiver mobile
     */
    val receiverMobileJsonPath: String?,
    /**
     * JSONPath for rsn
     */
    val rsnJsonPath: String?,
    /**
     * JSONPath for created
     */
    val createdJsonPath: String?,
    /**
     * JSONPath for modified
     */
    val modifiedJsonPath: String?,
    /**
     * Synchronizer bean name
     */
    val beanName: String?,
    /**
     * Synchronizer bean class
     */
    val beanClass: String?,
    /**
     * Synchronizer启用后将生成schedule记录
     * @see SyncSchedule
     */
    val enabled: Boolean = true,
    /**
     * 默认生产schedule
     */
    val scheduled: Boolean = true,
    /**
     * 是否批量保存数据，默认true
     */
    val batched: Boolean = true
) {

    fun beanClass(): Class<*> {
        return if (beanClass != null) Class.forName(beanClass) else PageSynchronizer::class.java
    }

    fun beanName(): String {
        return beanName ?: Class.forName(beanClass).simpleName.decapitalize() + shopCode
    }

    val countUrlPath: String
        get() {
            val paths = urlPath?.split(",") ?: throw IllegalArgumentException("Url paths is empty.")
            if (paths.size < 2) {
                throw IllegalArgumentException("Url paths need need have 2(${paths.size}) path.")
            }
            return paths[0]
        }

    val dataUrlPath: String
        get() {
            val paths = urlPath?.split(",") ?: throw IllegalArgumentException("Url paths is empty.")
            if (paths.size < 2) {
                throw IllegalArgumentException("Url paths need need have 2(${paths.size}) path.")
            }
            return paths[1]
        }
}

@TableName("sync_schedule")
data class SyncSchedule(
    @TableId
    val id: Long,
    /**
     * property id
     * @see SyncProperty
     */
    val propertyId: Long,
    /**
     * 门店编码
     */
    val shopCode: String?,
    /**
     * 开始时间，包含
     */
    val startTime: LocalDateTime,
    /**
     * 结束时间，不包含
     */
    val endTime: LocalDateTime,
    /**
     * 优先级，默认为0
     */
    val priority: Int = 0,
    /**
     * 是否同步完成
     */
    var completed: Boolean = false,
    /**
     * 创建时间，用于清理数据
     */
    val created: LocalDateTime
) {
    val startTimeValue: String
        get() = startTime.format(formatter)

    val endTimeValue: String
        get() = endTime.format(formatter)

    constructor(
        propertyId: Long,
        shopCode: String?,
        startTime: LocalDateTime,
        endTime: LocalDateTime
    ) : this(
        IdWorker.getId(), propertyId, shopCode, startTime, endTime, 0, false, LocalDateTime.now()
    )
}

/**
 * 数据包装类型
 */
@TableName("sync_document")
data class SyncDocument(
    @TableId
    var id: Long?,
    /**
     * property id
     * @see SyncProperty
     */
    var propertyId: Long?,
    /**
     * 门店编码
     */
    var shopCode: String?,
    /**
     * 单号
     */
    val sn: String?,
    /**
     * 关联单号
     */
    val rsn: String?,
    /**
     * 订单状态
     */
    val status: String?,
    /**
     * 收货人名称
     */
    val receiverName: String?,
    /**
     * 收货人手机号
     */
    val receiverMobile: String?,
    /**
     * 单据创建时间
     */
    val created: LocalDateTime?,
    /**
     * 单据修改时间
     */
    var modified: LocalDateTime?,
    /**
     * 数据
     */
    var data: String?,
    /**
     * 数据同步创建时间
     */
    var syncCreated: LocalDateTime?,
    /**
     * 数据同步修改时间
     */
    var syncModified: LocalDateTime?
) {

    @Suppress("unused")
    constructor() : this(
        null, null, null, null, null, null, null,
        null, null, null, null, null, null
    )

    constructor(
        sn: String,
        rsn: String?,
        status: String?,
        receiverName: String?,
        receiverMobile: String?,
        created: LocalDateTime?,
        modified: LocalDateTime?,
        data: String
    )
            : this(
        IdWorker.getId(), null, null,
        sn, rsn, status, receiverName, receiverMobile, created, modified, data, null, null
    ) {
        val now = LocalDateTime.now()
        this.syncCreated = now
        this.syncModified = now
    }
}